using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using JlPay.Sdk.Utils;

namespace JlPay.Sdk.Core
{
    /// <summary>
    /// 默认加密服务实现
    /// </summary>
    /// <param name="config">配置对象</param>
    public class DefaultCryptoService(Config config) : ICryptoService
    {
        private readonly Config _config = config ?? throw new ArgumentNullException(nameof(config));

        /// <summary>
        /// 加密请求
        /// </summary>
        /// <param name="request">请求对象</param>
        /// <returns>加密后的HTTP头部信息</returns>
        public Dictionary<string, string> EncryptRequest(object request)
        {
            var headers = new Dictionary<string, string>();

            if (_config.Debug)
            {
                Console.WriteLine("=== 开始加密请求 ===");
            }

            // 1. 检查是否需要加密
            if (!_config.AutoEncrypt)
            {
                // 即使未启用加密，仍然添加加密算法头，确保服务端识别请求类型
                headers["x-jlpay-crypto-alg"] = "SM2WithSM4";
                if (_config.Debug)
                {
                    Console.WriteLine("自动加密未启用，仅添加加密算法头");
                }
                return headers;
            }

            // 检查加密算法是否支持
            if (_config.CryptoAlg != "SM2WithSM4")
            {
                throw new InvalidOperationException($"不支持的加密算法: {_config.CryptoAlg}");
            }

            // 2. 获取敏感字段
            var sensitiveFields = GetSensitiveFields(request);
            if (sensitiveFields.Count == 0)
            {
                headers["x-jlpay-crypto-alg"] = "SM2WithSM4";
                if (_config.Debug)
                {
                    Console.WriteLine("未配置敏感字段，仅添加加密算法头");
                }
                return headers;
            }

            // 3. 生成并加密SM4密钥
            var (sm4Key, encryptedKeyBase64) = GenerateAndEncryptKey();

            // 4. 设置基本加密头
            headers["x-jlpay-crypto-alg"] = "SM2WithSM4";
            headers["x-jlpay-key"] = encryptedKeyBase64;

            // 5. 加密敏感字段
            EncryptSensitiveFieldsInRequest(request, sensitiveFields, sm4Key);

            if (_config.Debug)
            {
                Console.WriteLine("=== 加密请求完成 ===");
            }

            return headers;
        }

        /// <summary>
        /// 获取敏感字段列表
        /// </summary>
        /// <param name="request">请求对象</param>
        /// <returns>敏感字段列表</returns>
        private List<string> GetSensitiveFields(object request)
        {
            var sensitiveFields = new List<string>();
            
            if (request is ISensitiveFieldsProvider provider)
            {
                sensitiveFields = provider.GetSensitiveFields();
                if (_config.Debug)
                {
                    Console.WriteLine($"获取到敏感字段: {string.Join(", ", sensitiveFields)}");
                }
            }
            else
            {
                if (_config.Debug)
                {
                    Console.WriteLine("请求对象未实现ISensitiveFieldsProvider接口");
                }
            }
            
            return sensitiveFields;
        }

        /// <summary>
        /// 生成SM4密钥并使用SM2加密
        /// </summary>
        /// <returns>SM4密钥和加密后的Base64字符串</returns>
        private (byte[] sm4Key, string encryptedKeyBase64) GenerateAndEncryptKey()
        {
            // 生成SM4密钥
            var sm4Key = SmUtils.GenerateSM4Key();
            if (_config.Debug)
            {
                Console.WriteLine("成功生成SM4密钥");
            }

            // 使用SM2加密SM4密钥
            var encryptedKey = SmUtils.SM2Encrypt(sm4Key, _config.JlpayPublicKey);
            if (_config.Debug)
            {
                Console.WriteLine("成功使用SM2加密SM4密钥");
            }

            // 将加密后的密钥转为Base64字符串
            var encryptedKeyBase64 = SmUtils.ToBase64String(encryptedKey);

            return (sm4Key, encryptedKeyBase64);
        }

        /// <summary>
        /// 加密请求中的敏感字段
        /// </summary>
        /// <param name="request">请求对象</param>
        /// <param name="sensitiveFields">敏感字段列表</param>
        /// <param name="sm4Key">SM4密钥</param>
        private void EncryptSensitiveFieldsInRequest(object request, List<string> sensitiveFields, byte[] sm4Key)
        {
            if (request is IRawMapGetter rawMapGetter)
            {
                var (rawMap, exists) = rawMapGetter.GetRawMap();
                if (!exists || rawMap.Count == 0)
                {
                    if (_config.Debug)
                    {
                        Console.WriteLine("原始请求Map为空或不存在");
                    }
                    throw new InvalidOperationException("原始请求Map为空或不存在");
                }

                if (_config.Debug)
                {
                    Console.WriteLine("使用原始Map方式处理敏感字段");
                }
                EncryptFieldsInMap(rawMap, sensitiveFields, sm4Key);
            }
            else
            {
                if (_config.Debug)
                {
                    Console.WriteLine("请求对象未实现IRawMapGetter接口，无法修改原始请求");
                }
                throw new InvalidOperationException("请求对象未实现IRawMapGetter接口");
            }
        }

        /// <summary>
        /// 加密Map中的敏感字段
        /// </summary>
        /// <param name="data">数据Map</param>
        /// <param name="sensitiveFields">敏感字段列表</param>
        /// <param name="sm4Key">SM4密钥</param>
        private void EncryptFieldsInMap(Dictionary<string, object> data, List<string> sensitiveFields, byte[] sm4Key)
        {
            var modified = false;

            foreach (var fieldPath in sensitiveFields)
            {
                try
                {
                    EncryptFieldInMap(data, fieldPath, sm4Key);
                    modified = true;
                }
                catch (Exception ex)
                {
                    // 记录错误但继续处理其他字段
                    if (_config.Debug)
                    {
                        Console.WriteLine($"字段 {fieldPath} 加密失败: {ex.Message}");
                    }
                }
            }

            if (_config.Debug)
            {
                if (modified)
                {
                    Console.WriteLine("成功更新原始请求Map中的敏感字段");
                }
                else
                {
                    Console.WriteLine("未对原始请求Map进行任何更新");
                }
            }
        }

        /// <summary>
        /// 加密Map中的单个敏感字段
        /// </summary>
        /// <param name="data">数据Map</param>
        /// <param name="fieldPath">字段路径</param>
        /// <param name="sm4Key">SM4密钥</param>
        private void EncryptFieldInMap(Dictionary<string, object> data, string fieldPath, byte[] sm4Key)
        {
            if (_config.Debug)
            {
                Console.WriteLine($"=== 开始加密字段: {fieldPath} ===");
                Console.WriteLine($"原始数据顶级键: {string.Join(", ", data.Keys)}");
            }

            // 1. 获取字段值
            var value = GetNestedMapValue(data, fieldPath);
            if (value == null)
            {
                if (_config.Debug)
                {
                    Console.WriteLine($"字段 {fieldPath} 不存在或为空，跳过加密");
                }
                throw new InvalidOperationException($"字段路径 {fieldPath} 不存在");
            }

            // 2. 检查并加密字段值
            if (value is not string strValue || string.IsNullOrEmpty(strValue))
            {
                if (_config.Debug)
                {
                    Console.WriteLine($"字段 {fieldPath} 的值不是字符串或为空，跳过加密（值类型: {value?.GetType().Name}, 值: {value}）");
                }
                throw new InvalidOperationException($"字段 {fieldPath} 不是字符串或为空");
            }

            // 3. 加密字段值
            var encryptedValue = EncryptFieldValue(strValue, sm4Key);

            // 4. 设置加密后的值
            SetNestedMapValue(data, fieldPath, encryptedValue);

            if (_config.Debug)
            {
                Console.WriteLine($"成功加密并更新字段 {fieldPath}");
            }
        }



        /// <summary>
        /// 从嵌套的Map中获取值
        /// </summary>
        /// <param name="data">数据Map</param>
        /// <param name="path">字段路径</param>
        /// <returns>字段值</returns>
        private static object? GetNestedMapValue(Dictionary<string, object> data, string path)
        {
            var parts = path.Split('.');
            object? current = data;

            foreach (var part in parts)
            {
                if (current is Dictionary<string, object> currentDict)
                {
                    if (!currentDict.TryGetValue(part, out current))
                    {
                        return null;
                    }
                }
                else if (current is Dictionary<string, string> currentStringDict)
                {
                    if (!currentStringDict.TryGetValue(part, out var stringValue))
                    {
                        return null;
                    }
                    current = stringValue;
                }
                else if (current is JsonElement jsonElement && jsonElement.ValueKind == JsonValueKind.Object)
                {
                    if (!jsonElement.TryGetProperty(part, out var property))
                    {
                        return null;
                    }
                    current = property;
                }
                else
                {
                    return null;
                }
            }

            return current;
        }

        /// <summary>
        /// 设置嵌套Map中的字段值
        /// </summary>
        /// <param name="data">数据Map</param>
        /// <param name="path">字段路径</param>
        /// <param name="value">字段值</param>
        private static void SetNestedMapValue(Dictionary<string, object> data, string path, string value)
        {
            var parts = path.Split('.');
            var current = data;

            // 遍历路径中的每个部分，创建路径并设置值
            for (var i = 0; i < parts.Length; i++)
            {
                var part = parts[i];

                // 最后一个部分，直接设置值
                if (i == parts.Length - 1)
                {
                    current[part] = value;
                    return;
                }

                // 不是最后一部分，检查下一级是否存在，不存在则创建
                if (!current.TryGetValue(part, out var next) || next is not Dictionary<string, object> nextDict)
                {
                    nextDict = new Dictionary<string, object>();
                    current[part] = nextDict;
                }

                current = nextDict;
            }
        }

        /// <summary>
        /// 加密字段值
        /// </summary>
        /// <param name="value">原始值</param>
        /// <param name="sm4Key">SM4密钥</param>
        /// <returns>加密后的Base64字符串</returns>
        private static string EncryptFieldValue(string value, byte[] sm4Key)
        {
            if (string.IsNullOrEmpty(value))
            {
                return string.Empty;
            }

            // 使用SM4-ECB模式加密
            var encryptedBytes = SmUtils.SM4ECBEncrypt(System.Text.Encoding.UTF8.GetBytes(value), sm4Key);

            // 将加密结果转为Base64字符串
            return SmUtils.ToBase64String(encryptedBytes);
        }

        /// <summary>
        /// 解密响应
        /// </summary>
        /// <param name="response">响应对象</param>
        /// <param name="headers">HTTP头部信息</param>
        public void DecryptResponse(object response, Dictionary<string, string> headers)
        {
            // 如果未启用自动解密，则直接返回
            if (!_config.AutoDecrypt)
            {
                return;
            }

            // 检查是否需要解密
            if (!headers.TryGetValue("x-jlpay-crypto-alg", out var cryptoAlg) || string.IsNullOrEmpty(cryptoAlg))
            {
                // 没有加密，无需解密
                return;
            }

            // 检查加密算法是否支持
            if (cryptoAlg != "SM2WithSM4")
            {
                throw new InvalidOperationException($"不支持的加密算法: {cryptoAlg}");
            }

            // 获取加密密钥
            if (!headers.TryGetValue("x-jlpay-key", out var encryptedKeyBase64) || string.IsNullOrEmpty(encryptedKeyBase64))
            {
                return;
            }

            // 将Base64编码的密钥转为字节数组
            var encryptedKey = SmUtils.FromBase64String(encryptedKeyBase64);

            // 使用SM2解密SM4密钥
            var sm4Key = SmUtils.SM2Decrypt(encryptedKey, _config.PrivateKey);

            // 检查响应对象是否实现了ISensitiveFieldsProvider
            if (response is ISensitiveFieldsProvider provider)
            {
                var sensitiveFields = provider.GetSensitiveFields();
                // 直接解密响应对象中的敏感字段
                DecryptSensitiveFieldsInResponse(response, sensitiveFields, sm4Key);
            }
        }

        /// <summary>
        /// 解密响应数据
        /// </summary>
        /// <param name="response">响应对象</param>
        /// <param name="encryptedFields">加密字段映射</param>
        /// <param name="sm4Key">SM4密钥</param>
        private void DecryptResponseData(object response, Dictionary<string, string> encryptedFields, byte[] sm4Key)
        {
            if (_config.Debug)
            {
                Console.WriteLine("=== 开始解密响应数据 ===");
            }

            foreach (var kvp in encryptedFields)
            {
                var fieldPath = kvp.Key;
                var encryptedValue = kvp.Value;

                if (_config.Debug)
                {
                    Console.WriteLine($"正在解密字段: {fieldPath}");
                    Console.WriteLine($"加密值: {encryptedValue}");
                }

                try
                {
                    // 解密字段值
                    var decryptedValue = DecryptFieldValue(encryptedValue, sm4Key);

                    if (_config.Debug)
                    {
                        Console.WriteLine($"解密后的值: {decryptedValue}");
                    }

                    // 设置解密后的值到响应对象
                    SetNestedFieldValue(response, fieldPath, decryptedValue);

                    if (_config.Debug)
                    {
                        Console.WriteLine($"成功设置字段 {fieldPath} 的解密值");
                    }
                }
                catch (Exception ex)
                {
                    // 字段不存在或不可设置，记录错误但继续处理其他字段
                    if (_config.Debug)
                    {
                        Console.WriteLine($"设置解密字段 {fieldPath} 失败: {ex.Message}");
                        Console.WriteLine($"异常详情: {ex}");
                    }
                }
            }

            if (_config.Debug)
            {
                Console.WriteLine("=== 解密响应数据完成 ===");
            }
        }

        /// <summary>
        /// 解密字段值
        /// </summary>
        /// <param name="value">加密的Base64字符串</param>
        /// <param name="sm4Key">SM4密钥</param>
        /// <returns>解密后的原始值</returns>
        private static string DecryptFieldValue(string value, byte[] sm4Key)
        {
            if (string.IsNullOrEmpty(value))
            {
                return string.Empty;
            }

            // 将Base64字符串转为字节数组
            var encryptedBytes = SmUtils.FromBase64String(value);

            // 使用SM4-ECB模式解密
            var decryptedBytes = SmUtils.SM4ECBDecrypt(encryptedBytes, sm4Key);

            return System.Text.Encoding.UTF8.GetString(decryptedBytes);
        }

        /// <summary>
        /// 解密响应对象中的敏感字段
        /// </summary>
        /// <param name="response">响应对象</param>
        /// <param name="sensitiveFields">敏感字段列表</param>
        /// <param name="sm4Key">SM4密钥</param>
        private void DecryptSensitiveFieldsInResponse(object response, List<string> sensitiveFields, byte[] sm4Key)
        {
            foreach (var fieldPath in sensitiveFields)
            {
                try
                {
                    // 获取字段的当前值
                    var currentValue = GetNestedFieldValue(response, fieldPath);

                    // 如果字段值为空或不是字符串，跳过
                    if (currentValue == null || currentValue is not string strValue || string.IsNullOrEmpty(strValue))
                    {
                        continue;
                    }

                    // 尝试解密字段值
                    try
                    {
                        var decryptedValue = DecryptFieldValue(strValue, sm4Key);
                        // 设置解密后的值
                        SetNestedFieldValue(response, fieldPath, decryptedValue);
                    }
                    catch (Exception)
                    {
                        // 解密失败，可能是字段本身不是加密的，继续处理下一个字段
                    }
                }
                catch (Exception)
                {
                    // 处理字段时出错，继续处理下一个字段
                }
            }
        }

        /// <summary>
        /// 获取嵌套结构中的字段值
        /// </summary>
        /// <param name="obj">目标对象</param>
        /// <param name="fieldPath">字段路径</param>
        /// <returns>字段值</returns>
        private static object? GetNestedFieldValue(object obj, string fieldPath)
        {
            var parts = fieldPath.Split('.');
            var current = obj;
            
            for (var i = 0; i < parts.Length; i++)
            {
                var propertyName = SnakeToPascalCase(parts[i]);
                var property = current.GetType().GetProperty(propertyName);
                
                if (property == null)
                {
                    return null;
                }
                
                current = property.GetValue(current);
                if (current == null)
                {
                    return null;
                }
            }
            
            return current;
        }

        /// <summary>
        /// 设置嵌套结构中的字段值
        /// </summary>
        /// <param name="obj">目标对象</param>
        /// <param name="fieldPath">字段路径</param>
        /// <param name="value">字段值</param>
        private void SetNestedFieldValue(object obj, string fieldPath, string value)
        {
            var parts = fieldPath.Split('.');
            var current = obj;
            
            for (var i = 0; i < parts.Length - 1; i++)
            {
                var propertyName = SnakeToPascalCase(parts[i]);
                var property = current.GetType().GetProperty(propertyName);
                
                if (property == null)
                {
                    throw new InvalidOperationException($"属性 {propertyName} 不存在");
                }
                
                current = property.GetValue(current);
                if (current == null)
                {
                    throw new InvalidOperationException($"属性 {propertyName} 值为null");
                }
            }
            
            var targetPropertyName = SnakeToPascalCase(parts[^1]);
            var targetProperty = current.GetType().GetProperty(targetPropertyName);
            
            if (targetProperty == null || !targetProperty.CanWrite)
            {
                throw new InvalidOperationException($"属性 {targetPropertyName} 不存在或不可写");
            }
            
            targetProperty.SetValue(current, value);
        }

        /// <summary>
        /// snake_case转PascalCase
        /// </summary>
        /// <param name="snakeCase">snake_case字符串</param>
        /// <returns>PascalCase字符串</returns>
        private static string SnakeToPascalCase(string snakeCase)
        {
            if (string.IsNullOrEmpty(snakeCase))
                return snakeCase;
            
            var result = "";
            var nextUpper = true;
            
            for (int i = 0; i < snakeCase.Length; i++)
            {
                if (snakeCase[i] == '_')
                {
                    nextUpper = true;
                }
                else
                {
                    if (nextUpper)
                    {
                        result += char.ToUpper(snakeCase[i]);
                        nextUpper = false;
                    }
                    else
                    {
                        result += char.ToLower(snakeCase[i]);
                    }
                }
            }
            return result;
        }
    }
} 